Fix Linux so that it does not set a timeout if there are no pending
authorkfraser@localhost.localdomain <kfraser@localhost.localdomain>
Tue, 25 Jul 2006 16:01:49 +0000 (17:01 +0100)
committerkfraser@localhost.localdomain <kfraser@localhost.localdomain>
Tue, 25 Jul 2006 16:01:49 +0000 (17:01 +0100)
timers. Fix Xen so that it does not immediately fire a timer event if
it sees a very long timeout -- sometimes this means that there are
no pending timers.

Signed-off-by: Keir Fraser <keir@xensource.com>
linux-2.6-xen-sparse/arch/i386/kernel/time-xen.c
xen/common/schedule.c

index d21a9560b184f3cde568733b7ad38148d9234882..504d27d663d0b240259a1e7dbd20f50d20c35839 100644 (file)
@@ -958,11 +958,17 @@ u64 jiffies_to_st(unsigned long j)
        do {
                seq = read_seqbegin(&xtime_lock);
                delta = j - jiffies;
-               /* NB. The next check can trigger in some wrap-around cases,
-                * but that's ok: we'll just end up with a shorter timeout. */
-               if (delta < 1)
-                       delta = 1;
-               st = processed_system_time + (delta * (u64)NS_PER_TICK);
+               if (delta < 1) {
+                       /* Triggers in some wrap-around cases, but that's okay:
+                        * we just end up with a shorter timeout. */
+                       st = processed_system_time + NS_PER_TICK;
+               } else if (((unsigned long)delta >> (BITS_PER_LONG-3)) != 0) {
+                       /* Very long timeout means there is no pending timer.
+                        * We indicate this to Xen by passing zero timeout. */
+                       st = 0;
+               } else {
+                       st = processed_system_time + delta * (u64)NS_PER_TICK;
+               }
        } while (read_seqretry(&xtime_lock, seq));
 
        return st;
@@ -989,14 +995,15 @@ static void stop_hz_timer(void)
 
        smp_mb();
 
-       /* Leave ourselves in 'tick mode' if rcu or softirq or timer pending. */
+       /* Leave ourselves in tick mode if rcu or softirq or timer pending. */
        if (rcu_needs_cpu(cpu) || local_softirq_pending() ||
            (j = next_timer_interrupt(), time_before_eq(j, jiffies))) {
                cpu_clear(cpu, nohz_cpu_mask);
                j = jiffies + 1;
        }
 
-       BUG_ON(HYPERVISOR_set_timer_op(jiffies_to_st(j)) != 0);
+       if (HYPERVISOR_set_timer_op(jiffies_to_st(j)) != 0)
+               BUG();
 }
 
 static void start_hz_timer(void)
index cc9947e2798cb63bf27a0a22df6e37194797bb8b..df9f9dcc3f6f98b564965ed5f9038944edeb6f9d 100644 (file)
@@ -404,12 +404,15 @@ long do_set_timer_op(s_time_t timeout)
          * for timeouts wrapped negative, and for positive timeouts more than 
          * about 13 days in the future (2^50ns). The correct fix is to trigger 
          * an interrupt immediately (since Linux in fact has pending work to 
-         * do in this situation).
+         * do in this situation). However, older guests also set a long timeout
+         * when they have *no* pending timers at all: setting an immediate
+         * timeout in this case can burn a lot of CPU. We therefore go for a
+         * reasonable middleground of triggering a timer event in 100ms.
          */
         DPRINTK("Warning: huge timeout set by domain %d (vcpu %d):"
                 " %"PRIx64"\n",
                 v->domain->domain_id, v->vcpu_id, (uint64_t)timeout);
-        send_timer_event(v);
+        set_timer(&v->timer, NOW() + MILLISECS(100));
     }
     else
     {